# Greg Johnston
#  <greg.johnston@gmail.com>
#  log.py
#  The RitualLog class and its functions

from zipfile import ZipFile, ZIP_DEFLATED
import StringIO
import random

from csv import DictReader, DictWriter
from calc import *
from config import RitualConfig
from entry import RitualEntry

class RitualLog:
	"""This class represents one runner's entire running log."""
	def __init__(self):
		self.log = {} # Runs are indexed in this dict. Index is (date, number of that run on that day)
		self.courses = {} # Courses are indexed in this dict. Value is (distance, unit)
		self.config = RitualConfig()
		for key in self.log.keys():
			if self.log[key].unit == 0:
				print self.log[key].unit

	def __len__(self):
		return len(self.log)

	# Log entry functions

	def add_entry(self, date, course, distance, unit, time, shoes, notes, offset=None):
		"""Adds the entry it is passed to the log"""
		if not offset:
			offset = len(self.runs_on(date))
		entry = RitualEntry(course, distance, unit, time, shoes, notes)
		if not self.is_duplicate(entry):
			self.log[(date, offset)] = entry
		else: print "Caught a duplicate..."
		if course != "":
			self.add_course(course, distance, unit)
		return self.log

	def remove_entry(self, date, number):
		"""Removes an entry given its date and number"""
		del self.log[(date, number)]
		return self.log

	def is_duplicate(self, entry):
		"""Returns True if an exact copy of a RitualEntry is in our log, False otherwise"""
		for key in self.log.keys():
			if entry == self.log[key]:
				return True
			else:
				return False

	def remove_duplicates(self):
		"""Removes duplicate entries from the log"""
		tmp = []
		for key in self.log.keys():
			entry = self.log[key]
			if not entry in tmp:
				tmp += [entry]
			else:
				print "Removing a duplicate..."
				self.remove_entry(key[0], key[1])

	def entry_distance(self, entry):
		"""Returns the distance, given an entry info list"""
		name = entry.course
		if name != "" and self.course_exists(name):
			return self.course_distance(name)
		else:
			return (entry.distance, entry.unit)

	def runs_on(self, date):
		"""Returns a list of all the runs on the date passed"""
		so_far = []
		for tuple in self.log.keys():
			if tuple[0] == date:
				so_far += [tuple]
		return so_far

	def get_run(self, date, number):
		"""Returns the information on a run, given its date and number"""
		return self.log[(date, number)]

	def runs_week_of(self, date):
		"""Returns a list of the runs the week of the date passed"""
		dates = dates_week_of(date)
		so_far = []
		for date in dates:
			for entry in self.runs_on(date):
				so_far += [self.get_run(date, entry[1])]
		return so_far

	def mpw(self, date):
		"""Returns the number of miles in the week of the date passed"""
		so_far = 0
		for run in self.runs_week_of(date):
			tuple = self.entry_distance(run)
			if type(tuple[1]) is str:
				so_far += round(float(calc_miles(tuple[0], unit_code(tuple[1]))), 1)
			elif type(tuple[1]) is int:
				so_far += round(float(calc_miles(tuple[0], tuple[1])), 1)
		return so_far		

	def miles_in_week(self, week, year):
		"""Returns the number of miles in the ordinal Monday-started week passed"""
		so_far = 0
		for key in self.log.keys():
			if week_starting_on_monday(key[0]) == week and key[0].year == year:
				run = self.log[key]
				so_far += calc_miles(run.distance, run.unit)
		return round(so_far, 1)

	# Log course functions

	def add_course(self, name, distance, unit):
		"""Adds a course to the log given its name and distance (with units)"""
		self.courses[name] = (distance, unit)
		return self.courses

	def remove_course(self, name):
		"""Removes the course of the given name from the log"""
		del self.courses[name]
		return self.courses

	def course_distance(self, name):
		"""Returns the distance of the course whose name it is given"""
		if self.course_exists(name):
			return (self.courses[name][0], self.courses[name][1])
		else:
			return False

	def course_exists(self, name):
		"""Returns True if a course of that name exists, False otherwise"""
		try:
			course = self.courses[name]
			return True
		except:
			return False

	# Log shoe functions
	def runs_on_shoe(self, shoe):
		"""Returns all the runs on a particular shoe"""
		so_far = []
		for key in self.log.keys():
			if (self.log[key].shoes == shoe) and (not self.log[key] in so_far):
				so_far += [self.log[key]]
		return so_far

	def miles_on_shoe(self, shoe):
		"""Returns the number of miles on the shoe name passed"""
		so_far = 0
		for run in self.runs_on_shoe(shoe):
			so_far += calc_miles(run.distance, run.unit)
		return round(so_far, 1)


	# Log I/O functions
	fields = ("Date", "Number", "Course", "Distance", "Unit", "Time", "Shoes", "Notes")
	coursefields = ("Name", "Distance", "Unit")

	def read_file(self, file):
		"""Reads a .ritual file (zip file of two CSV files)"""
		if file == None:
			return False
		else:
			self.read_log(file)
			self.read_courses(file)
	
	def save_file(self, file):
		"""Saves a .ritual (zipped) file with log and courses"""
		zip = ZipFile(file, "w", ZIP_DEFLATED)
		zip.write(self.write_log(), "log.csv")
		zip.write(self.write_courses(), "course.csv")
		zip.close()
	
	def read_log(self, file):
		"""Reads the log from log.csv in a .ritual (zipped) file"""
		zip = ZipFile(file, "r")
		f = StringIO.StringIO(zip.read("log.csv"))
		reader = DictReader(f, self.fields)
		lines = []
		nodups = []
		for row in reader:
			lines = lines + [row]
		for entry in lines[1:]: # first row is headers
			if round(float(entry["Distance"]), 1) < 15 and entry["Unit"] == "m":
				entry["Unit"] == "miles"
			self.add_entry(string_date(entry["Date"]), entry["Course"],
						round(float(entry["Distance"]), 1), unit_code(entry["Unit"]),
						int(entry["Time"]), entry["Shoes"], entry["Notes"])
		f.close()
		zip.close()

	def write_log(self):
		"""Returns the name of a temporary file"""
		fname = self.config.config["tmp_dir"]+"log"+str(random.randint(0, 1000))+".csv"
		f = open(fname, "w")
		writer = DictWriter(f, self.fields)
		header = {}
		for field in self.fields:
			header[field] = field
		writer.writerow(header)
		for key in self.log:
			writer.writerow({"Date" : key[0].strftime("%m/%d/%Y"),
							"Number" : key[1],
							"Course" : self.log[key].course,
							"Distance" : self.log[key].distance,
							"Unit" : code_unit(self.log[key].unit),
							"Time" : self.log[key].time,
							"Shoes" : self.log[key].shoes,
							"Notes" : self.log[key].notes})
		f.close()
		return fname

	def read_courses(self, file):
		"""Reads the courses from a CSV file"""
		zip = ZipFile(file, "r")
		f = StringIO.StringIO(zip.read("course.csv"))
		reader = DictReader(f, self.coursefields)
		lines = []
		for row in reader:
			lines = lines + [row]
		for course in lines[1:]: # first row is headers
			self.add_course(course["Name"], course["Distance"], unit_code(course["Unit"]))
		f.close()
		zip.close()

	def write_courses(self):
		"""Returns the name of a temporary file"""
		fname = self.config.config["tmp_dir"]+"course"+str(random.randint(0, 1000))+".csv"
		f = open(fname, "w")
		writer = DictWriter(f, self.coursefields)
		header = {}
		for field in self.coursefields:
			header[field] = field
		writer.writerow(header)
		for key in self.courses:
			writer.writerow({"Name" : key,
							"Distance" : self.courses[key][0],
							"Unit" : code_unit(self.courses[key][1])})
		f.close()
		return fname

if __name__ == "__main__":
	log = RitualLog()
	log.read_file("/home/gjohnston/Documents/Running/greg.ritual")
	from datetime import date
	print log.mpw(date(2007, 11, 10))
